package paxos

import (
	"fmt"
	"log"
	"net"
	"net/rpc"
)

// Acceptor 接受者
type Acceptor struct {
	lis            net.Listener
	id             int         // 服务器id
	minProposal    int         // 接收者承诺的提案号
	acceptedNumber int         // 接受者接受的提案编号
	acceptedValue  interface{} // 接受者接受的提案值
	learners       []int       // 学习者列表
}

// Prepare 准备
func (a *Acceptor) Prepare(args *MsgArgs, reply *MsgReply) error {
	// 如果提案号大于最小提案号，则接受该提案，并返回该提案号和值
	if args.Number > a.minProposal {
		a.minProposal = args.Number
		reply.Number = a.acceptedNumber
		reply.Value = a.acceptedValue
		reply.Ok = true
	} else {
		reply.Ok = false
	}
	return nil
}

func (a *Acceptor) Accept(args *MsgArgs, reply *MsgReply) error {
	// 如果提案号大于最小提案号，则接受该提案，并返回该提案号和值，将提案号和值发送给学习者
	if args.Number >= a.minProposal {
		a.minProposal = args.Number
		a.acceptedNumber = args.Number
		a.acceptedValue = args.Value
		reply.Ok = true
		for _, lid := range a.learners {
			// 异步调用学习者
			go func(learner int) {
				addr := fmt.Sprintf("127.0.0.1:%d", learner)
				args := MsgArgs{
					From:   a.id,
					To:     learner,
					Number: args.Number,
					Value:  args.Value,
				}
				ok := call(addr, "Learner.Learn", &args, new(MsgReply))
				if !ok {
					return
				}
			}(lid)
		}
	} else {
		reply.Ok = false
	}
	return nil
}

func newAcceptor(id int, learners []int) *Acceptor {
	acceptor := &Acceptor{
		id:       id,
		learners: learners,
	}
	acceptor.server()
	return acceptor
}

func (a *Acceptor) server() {
	err := rpc.NewServer().Register(a)
	if err != nil {
		return
	}
	a.lis, err = net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", a.id))
	if err != nil {
		log.Fatal("listen error:", err)
	}
	go func() {
		for {
			conn, err := a.lis.Accept()
			if err != nil {
				continue
			}
			go rpc.ServeConn(conn)
		}
	}()
}

func (a *Acceptor) close() {
	_ = a.lis.Close()
}
